/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package batailledesardennes.util.geomFloat;

/**
 *
 * @author charly
 */
public abstract class Shape {

    public abstract boolean containsPoint(float x, float y);

    public final boolean containsPoint(Point p) {
        return containsPoint(p.getX(), p.getY());
    }

    public final boolean overlaps(Shape other) {
        if (containsPoint(other.getCenter())) {
            return true;
        }
        if (intersects(other)) {
            return true;
        }
        return false;
    }

    /**
     * Returns true if the recatanlge is fully contained in this shape
     * @param x
     * @param y
     * @param with
     * @param height
     * @return
     */
    public abstract boolean containsRect(float x, float y, float width, float height);

    public final boolean containsRect(Rectangle rect) {
        return containsRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
    }

    public abstract float getArea();

    /**
     * Fills the given array with the bounds.
     * lefX, botY, rightX, topY
     * A new array is returned if the given one is null
     * or not big enough
     * @param bounds
     * @return
     */
    public abstract float[] getBounds(float[] bounds);

    public final float[] getBounds() {
        return getBounds(null);
    }

    public final Rectangle getBoundsRect() {
        float[] bounds = getBounds();
        Rectangle r = new Rectangle(bounds[0], bounds[1], bounds[2] - bounds[0], bounds[3] - bounds[1]);
        return r;
    }

    public abstract Point getCenter();

    /**
     * Returns the closest intersection, if any, to pt0 (x0,y0)
     * Returns null otherwise
     * @param x0
     * @param y0
     * @param x1
     * @param y1
     * @return
     */
    public abstract Point getClosestIntersectionWithLine(float x0, float y0, float x1, float y1);

    /**
     *  Returns the closest intersection, if any, to pt0 (x0,y0)
     * Returns null otherwise
     * */
    public final Point getClosestIntersectionWithLine(Point p0, Point p1) {
        return getClosestIntersectionWithLine(p0.getX(), p0.getY(), p1.getX(), p1.getY());
    }

    public final Point getClosestPoint(float x, float y) {
        return getClosestPoint(x, y, 0);
    }

    public final Point getClosestPoint(Point p) {
        return getClosestPoint(p.getX(), p.getY());
    }

    public final Point getClosestPoint(Point p, float dist) {
        return getClosestPoint(p.getX(), p.getY(), dist);
    }

    public abstract Point getBoundPointFromCenterTowards(float x, float y);

    public final Point getBoundPointFromCenterTowards(Point p) {
        return getBoundPointFromCenterTowards(p.getX(), p.getY());
    }

    /**
     * Returns closest point at a distance dist from polygon boundaries
     * @param x
     * @param y
     * @param dist
     * @return
     */
    public abstract Point getClosestPoint(float x, float y, float dist);

    public abstract float getPerimeter();

    public abstract float getRadius();

    public boolean mayInteresectWith(Shape other) {
        float sumRadiusSq = getRadius() + other.getRadius();
        sumRadiusSq *= sumRadiusSq;
        if (getCenter().distanceSq(other.getCenter()) > sumRadiusSq) {
            return false;
        }
        return true;
    }

    public boolean mayIntersectWithLine(float x0, float y0, float x1, float y1) {
        float radius = getRadius();
        if (getCenter().distToSegSq(x0, y0, x1, y1) > radius * radius) {
            return false;
        }
        return true;
    }

    public final boolean mayIntersectWithLine(Point p0, Point p1) {
        return mayIntersectWithLine(p0.getX(), p0.getY(), p1.getX(), p1.getY());
    }

    public abstract boolean intersects(Shape other);

    public abstract boolean intersectsRect(float x, float y, float width, float height);

    public boolean intersectsRect(Rectangle rect) {
        return intersectsRect(rect.getX(), rect.getY(), rect.getWidth(), rect.getHeight());
    }

    public abstract boolean intersectsWithLineSegment(float x0, float y0, float x1, float y1);

    public final boolean intersectsWithLineSegment(Point p0, Point p1) {
        return intersectsWithLineSegment(p0.getX(), p0.getY(), p1.getX(), p1.getY());
    }

    public abstract Shape rotate(float angle, float x, float y);

    public final Shape rotate(float angle, Point p) {
        return rotate(angle, p.getX(), p.getY());
    }

    public final Shape rotate(float angle) {
        return rotate(angle, getCenter());
    }

    public abstract Shape scale(float sx, float sy, float x, float y);

    public final Shape scale(float sx, float sy, Point p) {
        return scale(sx, sy, p.getX(), p.getY());
    }

    public final Shape scale(float sx, float sy) {
        return scale(sx, sy, getCenter().getX(), getCenter().getY());
    }

    public final Shape scale(float factor) {
        return scale(factor, factor);
    }

    public final Shape scale(float factor, Point p) {
        return scale(factor, factor, p);
    }

    public final Shape scale(float factor, float x, float y) {
        return scale(factor, factor, x, y);
    }

    public abstract Shape translate(float x, float y);

    public abstract Shape translateTo(float x, float y);

    public final Shape translateTo(Point p) {
        return translateTo(p.getX(), p.getY());
    }

    public final Shape translateToOrigin() {
        return translateTo(0, 0);
    }

    public abstract Shape getCopy();
}
